﻿#include <Windows.h>
#include <iostream>
#include "../SDK/include/apx5200sa.h"
#include "Had5200BClass.h"
#include <string>
#include <cmath>
#include<fstream>
#include <cstdlib>


std::mutex HAD5200BClass::mx_;
int32_t HAD5200BClass::using_count_ = 0;

HAD5200BClass::HAD5200BClass(int32_t nBoardIndex)
{
	board_index_ = nBoardIndex;
	int32_t rtn = apx5200saInitialize();
	if (rtn != APX5200_STATUS_SUCCESS && rtn != APX5200_STATUS_INVALID_REQUEST) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saInitialize"));
	}
	apx5200saGetVersion(&version_);
	rtn = apx5200saGetBoardInf(board_index_, &board_info_);
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetBoardInf"));
	}
	rtn = apx5200saGetFpgaInfo(nBoardIndex, &fpga_inf_);
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetFpgaInfo"));
	}
	fpga_version_ = 0x000001FF & fpga_inf_.CompileVersion;
	std::unique_lock<std::mutex> lock{ mx_ };
	using_count_++;
}

HAD5200BClass::~HAD5200BClass()
{
	apx5200saRemoveBuffer(board_index_, 1);
	apx5200saRemoveBuffer(board_index_, 2);
	apx5200saAoReleaseBuffer(board_index_, 1);
	apx5200saAoReleaseBuffer(board_index_, 1);
	std::unique_lock<std::mutex> lock{mx_};
	if (--using_count_ == 0)
	{
		apx5200saFinalize();
	}
}

void HAD5200BClass::samplingStart()
{
	int32_t rtn = apx5200saGetOutputMode(board_index_, &sampling_mode_);
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetOutputMode"));
	}
	rtn = apx5200saStart(board_index_,3);
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saStart"));
	}
}

void HAD5200BClass::samplingStop()
{
	auto rtn = apx5200saStop(board_index_);
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saStop"));
	}
	sampling_mode_ = -1;
}

void HAD5200BClass::setSamplingParameter(const HAD5200BSamplingParam& p) const
{

	int32_t rtn;
	// Source Clock Setting
	if ((rtn = apx5200saSetClock(board_index_, ADC5200_CLOCKSOURCE_ONBOARD, p.ClockMask)) != APX5200_STATUS_SUCCESS)
	{
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetClock"));
	}
	// External Trigger Setting
	if ((rtn = apx5200saSetExtTrigger(board_index_,static_cast<int32_t>(p.InputEdge), static_cast<int32_t>(p.SyncMode))) != APX5200_STATUS_SUCCESS)
	{
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetExtTrigger"));
	}
	// Sampling Mode Setting
	if ((rtn = apx5200saSetSamplingMode(board_index_, ADC5200_SMP_MODE_SEQUENCE)) != APX5200_STATUS_SUCCESS)
	{
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetSamplingMode"));
	}
	// Board Parameter Setting
	if ((rtn = apx5200saSetBdParameter(board_index_, static_cast<int32_t>(p.Clock), ADC5200_INTERLEAVING_DISABLE)) != APX5200_STATUS_SUCCESS)
	{
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetBdParameter"));
	}
	// Output Mode Setting
	if ((rtn = apx5200saSetOutputMode(board_index_, static_cast<int32_t>(p.OutputMode))) != APX5200_STATUS_SUCCESS)
	{
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetOutputMode"));
	}
	// Capture Size Setting
	if ((rtn = apx5200saSetCaptureSize(board_index_, p.CaptureSize)) != APX5200_STATUS_SUCCESS)
	{
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetCaptureSize"));
	}
	// Resampling Size Setting
	int32_t window_size = 0;
	if (fpga_version_ < 3) {
		if ((rtn = apx5200saSetResamplingSize(board_index_, p.ResamplingSize)) != APX5200_STATUS_SUCCESS)
		{
			throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetResamplingSize"));
		}
		window_size = p.ResamplingSize;
	}
	else
	{
		auto fft_size = p.FftSize == 0 ? p.ResamplingSize : p.FftSize;
		if ((rtn = apx5200saSetResamplingSizeEx(board_index_, p.ResamplingSize,fft_size)) != APX5200_STATUS_SUCCESS)
		{
			throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetResamplingSizeEx"));
		}
		window_size = fft_size;
	}
	for (int ch = 1; ch <= 2; ch++)
	{
		const HAD5200BChParam* pch;
		if (ch == 1)
			pch = &p.Ch1Param;
		else
			pch = &p.Ch2Param;

		
		APX5200SA_INP inp;
		APX5200SA_TRG trig;
		inp.CaptureNum = pch->CaptureNum;
		inp.TotalNum = pch->TotalNum;
		inp.VoltageOffset = pch->VoltageOffset;
		trig.Source = ADC5200_TRG_SOURCE_EXT;
		trig.Action.DIMask =static_cast<int32_t>(pch->DiMask);
		trig.Action.Loop = 1;
		trig.Action.Offset = pch->Offset;

		// Channel Parameter Setting
		if ((rtn = apx5200saSetChParameter(board_index_, ch, &inp, &trig)) != APX5200_STATUS_SUCCESS)
		{
			throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetChParameter"));
		}

		// Buffer Allocate
		if ((rtn = apx5200saSetBuffer(board_index_, ch, p.BankNum)) != APX5200_STATUS_SUCCESS)
		{
			throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetBuffer"));
		}
	}
	// Resampling Enable Setting
	if ((rtn = apx5200saSetResamplingEnable(board_index_, static_cast<int32_t>(p.ResamplingEnable))) != APX5200_STATUS_SUCCESS)
	{
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetResamplingEnable"));
	}
	// FIR Enable Setting
	if ((rtn = apx5200saSetFIREnable(board_index_, static_cast<int32_t>(p.FIREnable))) != APX5200_STATUS_SUCCESS)
	{
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetFIREnable"));
	}
	// FIR Window Setting
	if (p.FIREnable == HAD5200BEnable::Enable) {
		if ((rtn = apx5200saSetFIRWindow(board_index_, const_cast<double*>(p.FIRWindow))) != APX5200_STATUS_SUCCESS)
		{
			throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetFIRWindow"));
		}
	}
	// Cross Threshold Setting
	if ((rtn = apx5200saSetCrossThreshold(board_index_, p.CrossThreshold)) != APX5200_STATUS_SUCCESS)
	{
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetCrossThreshold"));
	}
	// Cross Point Setting
	if ((rtn = apx5200saSetCrossPointSize(board_index_, p.CrossPointSize)) != APX5200_STATUS_SUCCESS)
	{
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetCrossPointSize"));
	}
	// Resampling Delay Setting
	if ((rtn = apx5200saSetResamplingDelay(board_index_, p.Delay)) != APX5200_STATUS_SUCCESS)
	{
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetResamplingDelay"));
	}
	// Window Setting
	if (!p.Window.empty())
	{
		
		if (p.Theta.size() != static_cast<size_t>(window_size)) {
			
			std::vector<double> theta(window_size, 0.0);
			if ((rtn = apx5200saSetWindowAndDispersion(board_index_, const_cast<double *>(p.Window.data()),
				theta.data())) != APX5200_STATUS_SUCCESS)
			{
				throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetWindowAndDispersion"));
			}
		}
		else {
			if ((rtn = apx5200saSetWindowAndDispersion(board_index_, const_cast<double *>(p.Window.data()),
				const_cast<double *>(p.Theta.data()))) != APX5200_STATUS_SUCCESS)
			{
				throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetWindowAndDispersion"));
			}
		}
	}
	if(p.CaptureMode==HAD5200BCaptureMode::RoundTrip)
	{
		// Window2 Setting
		if (!p.Window2.empty())
		{

			if (p.Theta2.size() != static_cast<size_t>(window_size)) {

				std::vector<double> theta(window_size, 0.0);
				if ((rtn = apx5200saSetWindowAndDispersionEx(board_index_, const_cast<double *>(p.Window2.data()),
					theta.data(),1)) != APX5200_STATUS_SUCCESS)
				{
					throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetWindowAndDispersionEx"));
				}
			}
			else {
				if ((rtn = apx5200saSetWindowAndDispersionEx(board_index_, const_cast<double *>(p.Window2.data()),
					const_cast<double *>(p.Theta2.data()),1)) != APX5200_STATUS_SUCCESS)
				{
					throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetWindowAndDispersionEx"));
				}
			}
			if ((rtn = apx5200saSetCaptureInterval(board_index_, p.CaptureInterval)) != APX5200_STATUS_SUCCESS)
			{
				throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetCaptureInterval"));
			}
		}
	}
	if ((rtn = apx5200saSetCaptureMode(board_index_, static_cast<int>(p.CaptureMode))) != APX5200_STATUS_SUCCESS)
	{
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetCaptureMode"));
	}
}



void HAD5200BClass::setSamplingParameter(const char * pt_file) const
{
	auto rtn = apx5200saSetBaseSetting(board_index_, const_cast<char*>(pt_file));
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetBaseSetting"));
	}
}

HAD5200BSamplingParam HAD5200BClass::getSamplingParameter() const
{
	HAD5200BSamplingParam param;
	int32_t rtn;
	int32_t int32_val1,int32_val2;
	uint32_t uint32_val;
	unsigned long long ull_val;
	double dbl_val;
	uint16_t* ptr_uint16_val;
	APX5200SA_TRG trg;
	APX5200SA_INP inp;

	if ((rtn = apx5200saGetBdParameter(board_index_,&int32_val1,&int32_val2)) != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetBdParameter"));
	}
	param.Clock = int32_val1;
	if ((rtn = apx5200saGetOutputMode(board_index_, &int32_val1)) != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetOutputMode"));
	}
	param.OutputMode = static_cast<HAD5200BOutputMode>(int32_val1);
	if ((rtn = apx5200saGetCaptureSize(board_index_, &uint32_val)) != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetCaptureSize"));
	}
	param.CaptureSize = uint32_val;


	int32_t window_size = 0;
	if (fpga_version_ < 3) {
		if ((rtn = apx5200saGetResamplingSize(board_index_, &int32_val1)) != APX5200_STATUS_SUCCESS) {
			throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetResamplingSize"));
		}
		param.ResamplingSize = int32_val1;
		param.FftSize = int32_val1;
		window_size = param.ResamplingSize;
	}
	else
	{
		if ((rtn = apx5200saGetResamplingSizeEx(board_index_, &int32_val1, &int32_val2)) != APX5200_STATUS_SUCCESS) {
			throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetResamplingSizeEx"));
		}
		param.ResamplingSize = int32_val1;
		param.FftSize = int32_val2;
		window_size = param.FftSize;
	}
	
	auto re = std::make_unique<double[]>(window_size);
	auto im = std::make_unique<double[]>(window_size);

	if ((rtn = apx5200saGetWindowAndDispersion(board_index_, re.get(), im.get())) != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetWindowAndDispersion"));
	}
	param.Window.reserve(window_size);
	param.Theta.reserve(window_size);
	auto all_zero = true;
	//imaginary num zero check
	for (int i = 0;i < window_size;i++) {
		if (im[i] != 0)
		{
			all_zero = false;
			break;
		}
	}
	if (all_zero) {
		//no dispersion compensation
		for (size_t i = 0; i <static_cast<uint32_t>(window_size); i++)
		{
			param.Window.push_back(re[i]);
			param.Theta.push_back(im[i]);
		}
	}
	else {
		//converts complex numbers to polar.
		for (size_t i = 0; i < static_cast<uint32_t>(window_size); i++)
		{
			param.Window.push_back(std::sqrt(re[i] * re[i] + im[i] + im[i]));
			if (im[i] == 0 && re[i] == 0)
				param.Theta.push_back(0);
			else
				param.Theta.push_back(std::atan2(im[i],re[i] ));
		}
	}

	if ((rtn = apx5200saGetResamplingEnable(board_index_, &int32_val1)) != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetFIREnable"));
	}
	param.ResamplingEnable = static_cast<HAD5200BEnable>(int32_val1);


	if ((rtn = apx5200saGetFIREnable(board_index_, &int32_val1)) != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetFIREnable"));
	}
	param.FIREnable = static_cast<HAD5200BEnable>(int32_val1);
	if ((rtn = apx5200saGetFIRWindow(board_index_, param.FIRWindow)) != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetFIRWindow"));
	}
	if ((rtn = apx5200saGetCrossThreshold(board_index_, &int32_val1)) != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetCrossThreshold"));
	}
	param.CrossThreshold = int32_val1;
	if ((rtn = apx5200saGetCrossPointSize(board_index_, &int32_val1)) != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetCrossPointSize"));
	}
	param.CrossPointSize = int32_val1;
	if ((rtn = apx5200saGetResamplingDelay(board_index_, &dbl_val)) != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetResamplingDelay"));
	}
	param.Delay = dbl_val;
	if ((rtn = apx5200saGetExtTrigger(board_index_,&int32_val1,&int32_val2)) != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetExtTrigger"));
	}
	param.InputEdge = static_cast<HAD5200BInputEdge>(int32_val1);
	param.SyncMode = static_cast<HAD5200BEnable>(int32_val2);
	if ((rtn = apx5200saGetClock(board_index_, &int32_val1, &uint32_val)) != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetClock"));
	}
	param.ClockMask = uint32_val;
	if ((rtn = apx5200saGetChParameter(board_index_,1,&inp, &trg )) != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetChParameter Ch1"));
	}
	param.Ch1Param.VoltageOffset = inp.VoltageOffset;
	param.Ch1Param.CaptureNum = inp.CaptureNum;
	param.Ch1Param.TotalNum = inp.TotalNum;
	param.Ch1Param.DiMask = static_cast<HAD5200BDiMaskFlag>(trg.Action.DIMask);
	param.Ch1Param.Offset = trg.Action.Offset;
	if ((rtn = apx5200saGetChParameter(board_index_, 2, &inp, &trg)) != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetChParameter Ch2"));
	}
	param.Ch2Param.VoltageOffset = inp.VoltageOffset;
	param.Ch2Param.CaptureNum = inp.CaptureNum;
	param.Ch2Param.TotalNum = inp.TotalNum;
	param.Ch2Param.DiMask = static_cast<HAD5200BDiMaskFlag>(trg.Action.DIMask);
	param.Ch2Param.Offset = trg.Action.Offset;

	if ((rtn = apx5200saGetBuffer(board_index_,1, &ull_val, &ptr_uint16_val)) != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetBuffer"));
	}
	if (param.OutputMode == HAD5200BOutputMode::Row) {
		param.BankNum = static_cast<uint32_t>(
			ull_val / (static_cast<uint64_t>(param.Ch1Param.CaptureNum) *param.CaptureSize * 2));
	}
	else {
		param.BankNum = static_cast<uint32_t>(
			ull_val / (static_cast<uint64_t>(param.Ch1Param.CaptureNum) * window_size * 2));
	}
	
	if ((rtn = apx5200saGetCaptureMode(board_index_, &int32_val1)) != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetCaptureMode"));
	}
	param.CaptureMode = static_cast<HAD5200BCaptureMode>(int32_val1);
	if(param.CaptureMode==HAD5200BCaptureMode::RoundTrip)
	{
		if ((rtn = apx5200saGetCaptureInterval(board_index_, &uint32_val)) != APX5200_STATUS_SUCCESS) {
			throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetCaptureMode"));
		}
		param.CaptureInterval = uint32_val;
		if ((rtn = apx5200saGetWindowAndDispersionEx(board_index_, re.get(), im.get(),1)) != APX5200_STATUS_SUCCESS) {
			throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetWindowAndDispersionEx"));
		}
		param.Window2.reserve(window_size);
		param.Theta2.reserve(window_size);
		all_zero = true;
		//imaginary num zero check
		for (int i = 0; i < window_size; i++) {
			if (im[i] != 0)
			{
				all_zero = false;
				break;
			}
		}
		if (all_zero) {
			//no dispersion compensation
			for (auto i = 0; i < window_size; i++)
			{
				param.Window2.push_back(re[i]);
				param.Theta2.push_back(im[i]);
			}
		}
		else {
			//converts complex numbers to polar.
			for (auto i = 0; i < window_size; i++)
			{
				param.Window2.push_back(std::sqrt(re[i] * re[i] + im[i] + im[i]));
				if (im[i] == 0 && re[i] == 0)
					param.Theta2.push_back(0);
				else
					param.Theta2.push_back(std::atan2(im[i], re[i]));
			}
		}
	}
	return param;

}

void HAD5200BClass::waitSampling(uint32_t timeout, APX5200SA_DATA& ch1, APX5200SA_DATA& ch2) const
//Gets error message from error code
{
	if (sampling_mode_ < 0) return;
	int32_t rtn = apx5200saWaitSampling(board_index_, 1, &ch1, timeout);
	if (rtn && rtn!= APX5200_STATUS_ABNORMAL) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saWaitSampling Ch1"));
		
	}
	if (sampling_mode_ == 0 || sampling_mode_ == 2) {
		rtn = apx5200saWaitSampling(board_index_, 2, &ch2, timeout);
		if (rtn && rtn != APX5200_STATUS_ABNORMAL) {
			throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saWaitSampling Ch2"));
		}
	}
	else {
		ch2.pBuffer = nullptr;
		ch2.status = 0;
	}
}

void HAD5200BClass::setAoParameter(int32_t ch, HAD5200BAoParam & p)
{
	if (ch != 1 && ch != 2) {
		throw HAD5200BException(HAD5200BException::HAD5200B_ERROR_INVALID_PARAM,
			getWhatArg(HAD5200BException::HAD5200B_ERROR_ALLOCATE_MEMORY, "setAoParameter"));
	}
	AO_5200_PARAM param;
	void* ptr;
	uint16_t* pAo;
	param.AoMode = static_cast<uint32_t>(p.Mode);
	param.WaitCount = p.WaitCount;
	param.Size =static_cast<uint32_t>(p.AoData.size());
	
	int32_t rtn = apx5200saAoReleaseBuffer(board_index_, ch);
	if (rtn != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saAoReleaseBuffer"));
	}

	//allocates data size plus 32byte
	if (ch == 1)
	{
		this->ao_ch1_buffer_ = std::unique_ptr<uint16_t[]>(new uint16_t[param.Size + 16]);
		ptr = ao_ch1_buffer_.get();
	}
	else {
		this->ao_ch2_buffer_ = std::unique_ptr<uint16_t[]>(new uint16_t[param.Size + 16]);
		ptr = ao_ch2_buffer_.get();
	}
	auto space = (param.Size + 16) * sizeof(uint16_t);
	if (!std::align(32, sizeof(uint16_t) * param.Size, ptr, space)) {
		throw HAD5200BException(HAD5200BException::HAD5200B_ERROR_ALLOCATE_MEMORY, 
			getWhatArg(HAD5200BException::HAD5200B_ERROR_ALLOCATE_MEMORY, "setAoParameter" ));
	}

	if (ch == 1) {
		ao_ch1_start_ptr_ = reinterpret_cast<uint16_t *>(ptr);
		param.pAoData = ao_ch1_start_ptr_;
		pAo = ao_ch1_start_ptr_;
	}
	else {
		ao_ch2_start_ptr_ = reinterpret_cast<uint16_t *>(ptr);
		param.pAoData = ao_ch2_start_ptr_;
		pAo = ao_ch2_start_ptr_;
	}
	//set Ao value
	for (const auto& ao : p.AoData) {
		*pAo++ = static_cast<uint16_t>((ao * 32768.0 / 5000.0) + 32768.0);
	}

	rtn = apx5200saSetAoParameter(board_index_, ch, &param);
	if (rtn != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetAoParameter"));
	}
}

void HAD5200BClass::startAo(int32_t ch) const
{
	auto rtn = apx5200saAoStart(board_index_, ch);
	if (rtn != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saAoStart"));
	}
}

void HAD5200BClass::stopAo(int32_t ch) const
{
	auto rtn = apx5200saAoStop(board_index_, ch);
	if (rtn != APX5200_STATUS_SUCCESS) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saAoStop"));
	}
}

void HAD5200BClass::getAoValue(double& ch1, double& ch2) const
{
    int32_t status;
    auto rtn = apx5200saGetAoStatus(board_index_,&status,&ch1,&ch2 );
    if (rtn) {
        throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetAoStatus"));
    }
    else if (status) {

    }
}

void HAD5200BClass::setPositionSetting( HAD5200PositionSetting const & posSetting) const noexcept
{
	apx5200saSetPositionSetting(board_index_, posSetting);
}

void HAD5200BClass::getPositionSetting(HAD5200PositionSetting& posSetting) const
{
	auto rtn = apx5200saGetPositionSetting(board_index_, posSetting);
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetPositionSetting"));
	}
}

void HAD5200BClass::movePosition(HAD5200_2D dist) const
{
	auto rtn = apx5200saMovePosition(board_index_,dist);
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saMovePosition"));
	}
}

void HAD5200BClass::getPosition(HAD5200_2D& position) const
{
	auto rtn = apx5200saGetPosition(board_index_, position);
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetPosition"));
	}
}

void HAD5200BClass::setBscanSetting(HAD5200BscanSetting & bScan) const
{
	auto rtn = apx5200saSetBscanSetting(board_index_, bScan);
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetBscanSetting"));
	}
}

void HAD5200BClass::getBscanSetting(HAD5200BscanSetting& bScan) const
{
	auto rtn = apx5200saGetBscanSetting(board_index_, bScan);
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetBscanSetting"));
	}
}

void HAD5200BClass::startBScan()
{
	auto rtn = apx5200saStartBscan(board_index_);
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saStartBscan"));
	}
	sampling_mode_ = 4;
}

void HAD5200BClass::stopBScan()
{
	auto rtn = apx5200saStop(board_index_);
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saStop"));
	}
	sampling_mode_ = -1;
}

void HAD5200BClass::waitBscanData(uint32_t timeout, APX5200SA_DATA& status, float*& bscan, uint32_t& bscan_size,
                                  uint32_t& depth_size) const
{
	auto rtn = apx5200saWaitBscanData2(board_index_,timeout,status, bscan,bscan_size, depth_size);
	if (rtn && rtn != APX5200_STATUS_ABNORMAL) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetBscanData2"));
	}
}

std::vector<float> HAD5200BClass::getBscanData(const APX5200SA_DATA& status,  unsigned& depthSize) const
{
	HAD5200BscanSetting bscan_setting;
	getBscanSetting(bscan_setting);
	auto size = bscan_setting.PointNum*((status.SegmentSize / 2)-bscan_setting.DepthOffset);
	std::vector<float> bscan(size);
	auto pbscan = bscan.data();
	auto rtn = apx5200saGetBscanData(board_index_, status, pbscan, size, depthSize);
	if (rtn && rtn != APX5200_STATUS_ABNORMAL) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetBscanData"));
	}
	return bscan;
}

std::vector<HAD5200_2D> HAD5200BClass::getBscanAoValues() const
{
	HAD5200_2D* ao_data=nullptr;
	size_t size;
	auto rtn = apx5200saGetBscanAoValues(board_index_, ao_data,size,true);
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetBscanData2"));
	}
	auto vector = std::vector<HAD5200_2D>(ao_data, ao_data + size);
	CoTaskMemFree(ao_data);
	return  vector;
}

void HAD5200BClass::setCscanSetting(HAD5200CscanSetting& cscan) const
{
	auto rtn = apx5200saSetCscanSetting(board_index_, cscan);
	if (rtn ) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saSetCscanSetting"));
	}
}

void HAD5200BClass::getCscanSetting(HAD5200CscanSetting& cscan) const
{
	auto rtn = apx5200saGetCscanSetting(board_index_, cscan);
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetCscanSetting"));
	}
}

void HAD5200BClass::startCscan()
{
	auto rtn = apx5200saStartCscan(board_index_);
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saStartCscan"));
	}
	sampling_mode_ = 4;
}

void HAD5200BClass::stopCscan()
{
	auto rtn = apx5200saStopCscan(board_index_);
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saStopCscan"));
	}
	sampling_mode_ = -1;
}

void HAD5200BClass::getCscanDataSize(uint32_t& depth_size, uint32_t& line_number, uint32_t& line_points) const
{
	auto rtn = apx5200saGetCscanDataSize(board_index_, depth_size, line_number, line_points);
	if (rtn && rtn != APX5200_STATUS_ABNORMAL) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetCscanDataSize"));
	}
}

void HAD5200BClass::waitCscanData(uint32_t timeout, APX5200SA_DATA& status, float* cscan, uint32_t& cscan_size, uint32_t& depth_size, uint32_t& segment_index) const
{
	auto rtn = apx5200saWaitCscanData(board_index_, timeout, status, cscan, cscan_size, depth_size,segment_index);
	if (rtn && rtn != APX5200_STATUS_ABNORMAL) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetCscanData"));
	}
}

void HAD5200BClass::waitCscanData2(uint32_t timeout, APX5200SA_DATA& status, float*& cscan, uint32_t& cscan_size, uint32_t& depth_size, uint32_t& segment_index) const
{
	auto rtn = apx5200saWaitCscanData2(board_index_, timeout, status, cscan, cscan_size, depth_size, segment_index);
	if (rtn && rtn != APX5200_STATUS_ABNORMAL) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetCscanData2"));
	}
}

uint32_t HAD5200BClass::getVersion() const noexcept
{ return version_; }

ADC5200_BD_INF HAD5200BClass::getBoardInfo(int32_t boardIndex)
{
	ADC5200_BD_INF boardInfo;
	bool initializedAlready = false;
	int32_t
	rtn = apx5200saInitialize();
	if (rtn == APX5200_STATUS_INVALID_REQUEST)
		initializedAlready = true;
	else if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saInitialize"));
	}

	rtn = apx5200saGetBoardInf(boardIndex, &boardInfo);
	
	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetBoardInf"));
	}
	if (!initializedAlready)
		apx5200saFinalize();
	return boardInfo;
}

uint32_t HAD5200BClass::getFpgaConfiguration(int32_t boardIndex)
{
	uint32_t config;
	bool initializedAlready = false;
	int32_t rtn = apx5200saInitialize();
	if (rtn == APX5200_STATUS_INVALID_REQUEST)
		initializedAlready = true;
	else if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saInitialize"));
	}

	rtn = apx5200saGetFpgaConfiguration(boardIndex, &config);

	if (rtn) {
		throw HAD5200BException(rtn, getWhatArg(rtn, "apx5200saGetFpgaConfigration"));
	}
	if (!initializedAlready)
		apx5200saFinalize();
	return config;
}

std::vector<uint16_t> HAD5200BClass::getSamplingData(const APX5200SA_DATA & data) noexcept
{
	if (data.Size <= 0)
		return {};
	return { data.pBuffer,data.pBuffer + data.Size };
}

std::vector<float> HAD5200BClass::getSamplingFloatData(const APX5200SA_DATA & data) noexcept
{
	if (data.Size <= 0)
		return {};
	const auto buff = reinterpret_cast<float*>(data.pBuffer);
	return{ buff,buff + static_cast<size_t>(data.Size / 2) };
}

std::string HAD5200BClass::getWhatArg(int32_t errorCode, const char* functionName)
{
	char message[MAX_ERROR_MESSAGE_LEN];
#pragma region Error message
	switch (errorCode)
	{
	case APX5200_STATUS_NOT_FILEOPEN:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
			"%s Error %s", functionName,
			"File Open Error" );

		break;
	case APX5200_STATUS_NOT_SET_PARAM:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		"Required parameter is not set.");
		break;
	case APX5200_STATUS_NOT_ENOUGH_PARAM:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "There are not enough parameters for the read file.");
		break;
	case APX5200_STATUS_UNPREPARED_SCAN:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
			"%s Error %s", functionName,
			"Has not configured scan setting.");
		break;
	case APX5200_STATUS_CNAT_SET_AOBUFFER:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
			"%s Error %s", functionName,
			"Can't set ao buffer because of trace data size over.");
		break;
	case APX5200_STATUS_POSITION_OVERRANGE:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
			"%s Error %s", functionName,
			"Can't set ao buffer because of trace data size over.");
		break;
	case APX5200_STATUS_VOLTAGE_OVERRANGE:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
			"%s Error %s", functionName,
			"Converted ao value is out of valid range.");
		break;
	case APX5200_STATUS_ERR_MEM_COPY:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
			"%s Error %s", functionName,
			"Error has occurred when data copy.");
		break;
	case APX5200_STATUS_ERR_MEM_ALLOC:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
			"%s Error %s", functionName,
			"Memory allocation failed.");
		break;
	case APX5200_STATUS_ERR_ARRAY_SIZE:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
			"%s Error %s", functionName,
			"Lack of the array size.");
		break;
	case APX5200_STATUS_UNMATCH_CAP_SIZE:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
			"%s Error %s", functionName,
			"The actual and excepted capture data size is not matched.");
		break;
	case APX5200_STATUS_ERR_AO_ARRAY_SIZE:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
			"%s Error %s", functionName,
			"Insufficient AO buffer size.");
		break;
	case APX5200_STATUS_ERR_RETURNED_NULL:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
			"%s Error %s", functionName,
			"apx5200saWaitSampling returned pBuffer address is null. It is possible that acquisition settings have any problems.");
		break;
	case APX5200_STATUS_ARRAY_ORDER_ILLEGAL:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
			"%s Error %s", functionName,
			"The sequence order of Window and Theta is incorrect.");
		break;
	case APX5200_STATUS_INVALID_CAPTURENUM:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
			"%s Error %s", functionName,
			"CaptureNum is not set to an even value.  In RoundTrip mode, CaptureNum must be set to an even value.");
		break;
	case APX5200_STATUS_UNEXECUTED_FUNCTION:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
			"%s Error %s", functionName,
			"A mandatory function has not been executed.");
		break;
	case APX5200_STATUS_INCOMPATI_FPGA_VER:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
			"%s Error %s", functionName,
			"Can't execute that function due to incompatible of FPGA version");
		break;

	case APX5200_STATUS_SUCCESS:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Success");
		break;
	case APX5200_STATUS_ERROR:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Unspecified runtime error. ");
		break;
	case APX5200_STATUS_NOT_INITIALIZED:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Module not initialized; e.g. An initialize method was not called before handling, or cannot establish a connection between devices(a frame grabber and a camera). ");
		break;
	case APX5200_STATUS_NOT_IMPLEMENTED:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Requested operation not implemented; e.g. No supported event or has no functions on a buffer module. ");
		break;
	case APX5200_STATUS_RESOURCE_IN_USE:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Requested resource is already in use by another operator. Requested operation is not allowed; e.g. a remote device is opened by another client. ");
		break;
	case APX5200_STATUS_ACCESS_DENIED:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Requested operation is not allowed; e.g. a remote device is opened by another client. ");
		break;
	case APX5200_STATUS_INVALID_HANDLE:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Given handle does not support the operation; e.g. function call on wrong handle or NULL pointer. ");
		break;
	case APX5200_STATUS_INVALID_ID:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "ID could not be connected to a resource; e.g. a device with the given ID is currently not available. ");
		break;
	case APX5200_STATUS_NO_DATA:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "The function has no data to work on. ");
		break;
	case APX5200_STATUS_INVALID_PARAMETER:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "One of the parameter given was not valid or out of range and none of the error codes above fits. ");
		break;
	case APX5200_STATUS_IO:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Communication error has occurred; e.g., A read and/or write operation to a remote device is failed.  Or To access to the physical module 'PCI'. ");
		break;
	case APX5200_STATUS_TIMEOUT:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "An operation is timeout time period expired before it could be completed. ");
		break;
	case APX5200_STATUS_ABORT:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "An operation has been aborted before it could be completed. For example a wait operation through a waiting any event. ");
		break;
	case APX5200_STATUS_INVALID_BUFFER:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "No buffer announced for an acquisition or one or more buffers of the acquisition with invalid buffer size. ");
		break;
	case APX5200_STATUS_NOT_AVAILABLE:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Resource or information is not available at a given time in a current state. ");
		break;
	case APX5200_STATUS_INVALID_ADDRESS:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "A given address is out of range or invalid for internal reasons. ");
		break;
	case APX5200_STATUS_CUSTOM_ID:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Any error smaller or equal than -10000 is implementation specific. If a Consumer application receives such an error number it should react as if it would be a generic runtime error. ");
		break;
	case APX5200_STATUS_CONNECT_LOST:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "A connection is lost.  e.g., a physical cable plug-off, or stopped supply a power with a camera device");
		break;
	case APX5200_STATUS_SHORTAGE_RESOURCE:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "A resources is shortage.  e.g., a physical continuous , remapping memory, an image buffer, a FIFO memory or a storage.");
		break;
	case APX5200_STATUS_OUT_OF_RANGE:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "One, or some parameter(s) was out of range . the parameter(s) exceeded by max or less than min. ");
		break;
	case APX5200_STATUS_INVALID_XFER:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "the transfer parameters(DMA script, buffer address/list and the size of an image) for an acquisition is invalid. ");
		break;
	case APX5200_STATUS_UNABLE_READ:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Reading error has occurred; e.g., A read operation to a remote(e.g., Camera) or a local device(e.g., PCI) is failed.  ");
		break;
	case APX5200_STATUS_UNABLE_WRITE:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Writing error has occurred; e.g., A write operation to a remote(e.g., Camera) or a local device(e.g., PCI) is failed.  ");
		break;
	case APX5200_STATUS_NOT_NOTIFIED:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Announce or receive events error has occurred; e.g., Notification (e.g., the end of frame, interrupt GPIN or received message ) to a user is failed.  ");
		break;
	case APX5200_STATUS_UNABLE_ALLOCATE:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Allocation error related to the resource, handle, or array. ");
		break;
	case APX5200_STATUS_IN_PROGRESS:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Requested operation already in progress ");
		break;
	case APX5200_STATUS_NOT_EXCLUSIVE:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Exclusive error ");
		break;
	case APX5200_STATUS_INVALID_REQUEST:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Invalid Request Error ");
		break;
	case APX5200_STATUS_NO_TARGET_BOARD:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "No Target Board ");
		break;
	case APX5200_STATUS_ALREADY_SAMPLING:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Already Sampling ");
		break;
	case APX5200_STATUS_ALREADY_AO_OUTPUT:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Already Ao Output ");
		break;
	case APX5200_STATUS_PLL_NOT_SUPPORT:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "PLL Not Support Clock  ");
		break;
	case APX5200_STATUS_CLOCK_UNSTABLE:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "PLL Clock Unstable");
		break;
	case APX5200_STATUS_ABNORMAL:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Sampling Error Abnormal ");
		break;
	case APX5200_STATUS_SAMPLING_STOPPED:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Sampling DMA Already Stopped ");
		break;
	case APX5200_STATUS_HW_DAMAGES:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Hard Ware Temperature Over ");
		break;
	case APX5200_STATUS_MEM_FREE_ERROR:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "DMA Memory Free Error ");
		break;
	case APX5200_STATUS_NO_SET_BUFFER:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Not Set Buffer Error ");
		break;
	case APX5200_STATUS_INVALID_INDEX:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Select Index Invalid Error ");
		break;
	case APX5200_STATUS_AO_BUFFSET_ERROR:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "AO Buffer Setting Error ");
		break;

	case HAD5200BException::HAD5200B_ERROR_INVALID_PARAM:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
			"%s Error %s", functionName,
			"Set invalid parameter ");
		break;
	case HAD5200BException::HAD5200B_ERROR_ALLOCATE_MEMORY:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
			"%s Error %s", functionName,
			"AO Buffer Allocation Error ");
		break;
    case HAD5200BException::HAD5200B_ERROR_ANALOG_OUTPUT:
        snprintf(message, MAX_ERROR_MESSAGE_LEN,
            "%s Error %s", functionName,
            "Analog output error ");
        break;


	default:
		snprintf(message, MAX_ERROR_MESSAGE_LEN,
		"%s Error %s", functionName,
		 "Unknown Error ");
		break;
	}
#pragma endregion
	return { message };
}

